#!/usr/bin/python

# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU Library General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
#
# Copyright Red Hat Inc. 2007, 2008
#
# Author: Izhar Firdaus <izhar@fedoraproject.com>
# Examples:
#
# yum oci-install oneclickinstall-XML-metadata-file
# yum oci-query details oneclickinstall-XML-metadata-file
# yum oci-query repositories oneclickinstall-XML-metadata-file
# yum oci-query packages oneclickinstall-XML-metadata-file
#


from xml.etree.ElementTree import ElementTree,Element
import re

from yum.plugins import PluginYumExit,TYPE_CORE,TYPE_INTERACTIVE
from yum import logginglevels
import yum.i18n, tempfile
_ = yum.i18n._

ocins='http://opensuse.org/Standards/One_Click_Install'
#distro='fedora'
#release='9'

## Use this for now
distro='openSUSE'
release='Factory'


requires_api_version = '2.5'
plugin_type = (TYPE_INTERACTIVE,)


def attrsfilter(elements,keywords,operation='or'):
   """
   Function to filter elements by its attributes

   @param   elements      A list of elements to be filtered	
   @param   keywords      A dictionary containing required tags and its values
   @param   operation     A string, either 'or' or 'and', for operation selection

   @return              A list of filtered elements by attributes
   """
   retval = []
   if not keywords: return elements
   if operation == 'or':
       for element in elements:
           for key in keywords:
               if element.attrib.has_key(key):
                  if element.attrib[key] == keywords[key]:
                     retval.append(element)
                     break
   elif operation == 'and':
       for element in elements:
           for key in keywords:
               if not element.attrib.has_key(key): break
               if element.attrib[key] != keywords[key]: break
           retval.append(element)
   return retval

def get_element_tag(element):
    """
    Function to get the element tag without namespace

    @param   element    An element which will be extracted the tag name

    @return             A string, containing the tag name without namespace
    """
    tag = re.match('{.*?}(.*)',element.tag)
    if tag:
       return tag.group(1)
    else:
       return tag

def get_child_value(element,childname):
    """
    Function to get the value of an element's child element

    @param  element    An element to be queried
    @param  childname  A string, containing the tagname of the child

    @return            A string containing child's text value if child is a leaf element, 
                       or a ElementTree element if child is a parent to 1 or more elements
    """
    for c in element.getchildren():
        if get_element_tag(c) == childname:
           if c.getchildren():
             return c
           else:
             return c.text


def add_tmp_repo(base,name,repourls):
    """
    Function to add temporary repository

    @param  base      The YumBase object for the operation
    @param  name      The repository name
    @param  repourls  A list of urls for the repository

    @return           None
    """
    tfo = tempfile.NamedTemporaryFile()
    repoid = name
    repoconfig = """
[%s]
name=%s
failovermethod=priority
baseurl=%s
enabled=1
""" % (name,name,'\n'.join(repourls))
    tfo.file.write(repoconfig)
    tfo.file.close()
    base.getReposFromConfigFile(tfo.name)

class OCIMetapackage:
   """
   Parser class for OCI Metapackage XML.
   """
   def __init__(self,element,distversion):
       """
       @param   element       ElementTree object which stores the XML file
       @param   distversion   A string containing '$distro $release'

       @return                None
       """
       self.element = element
       self.distversion = distversion

   def getGroup(self):
       """
       Function to get Group element from OCI Metadata

       @return    'group' Element of the current distversion
       """
       for group in self.element.getiterator('{%s}%s' % (ocins,'group')):
           if group.attrib['distversion'] == self.distversion:
              return group

   def getRepositories(self,*args,**kwargs):
       """
       Function to get 'repository' elements from the current distversion group

       @kwargs     Keyword arguments for filtering results

       @return     A list containing a filtered list of 'repository' Elements
       """
       repos = self.getGroup().getiterator('{%s}%s' % (ocins,'repository'))
       return attrsfilter(repos,kwargs)

   def getProducts(self,*args,**kwargs):
       """
       Function to get 'product' elements from the current distversion group

       @kwargs     Keyword arguments for filtering results

       @return     A list containing a filtered list of 'product' Elements
       """
       products = self.getGroup().getiterator('{%s}%s' % (ocins,'product'))
       return attrsfilter(products,kwargs)

   def getRepoURL(self,reponame):
       """
       Function to get 'url' values from current distversion group, and requested reponame

       @param   reponame   The repository name

       @return             A list of URLs from the requested repository, sorted higher score first
       """
       repos = self.getRepositories()
       urls = []
       for r in repos:
           if get_child_value(r,'name') == reponame:
              for url in r.getiterator('{%s}%s' % (ocins,'url')):
                  priority = 0
                  if url.attrib.has_key('score'): priority = int(url.attrib['score'])
                  urls.append((url.text.strip(),priority))
       def sorturl(x,y):
           if x[1] <= y[1]:
              return +1
           else:
              return -1
       urls.sort(sorturl)
       return [ url[0] for url in urls ]

class OCIQuery:
    def __init__(self,distro,release):
        self.distro = distro
        self.release = release

    def getNames(self):
        return ['oci-query']

    def getUsage(self):
        return "[details|repositories|packages] [OCI file]"

    def getSummary(self):
        return "Query data from OneClickInstall metadata"

    def doCheck(self, base, basecmd, extcmds):
        pass

    def getRepos(self): # so we can act as a "conduit"
        return self.repos

    def show_pkg(self, msg, pkg, md, disp=None):
        print msg, pkg, md

    def show_pkg_exit(self):
        pass

    def doCommand(self, base, basecmd, extcmds):
        action = extcmds[0]
        param = extcmds[1:]
        metapackages = []
        for mp in param:
            et = ElementTree(Element('metapackage'),open(mp))
            metapackages.append(OCIMetapackage(et,'%s %s' % (self.distro,self.release)))

        if action == 'details':
           for oci in metapackages:
               print '======================='
               print 'Metadata'
               print '======================='
               for c in oci.getGroup().getchildren():
                   tagname = get_element_tag(c)
                   if tagname in ['name','summary','description']:
                      print tagname,':',c.text
        if action == 'repositories':
           for oci in metapackages:
               print '========================'
               print 'Repositories'
               print '========================'
               for r in oci.getRepositories():
                   print get_child_value(r,'name')
                   print oci.getRepoURL(get_child_value(r,'name'))
        if action == 'packages':
           for oci in metapackages:
               print '========================'
               print 'Packages'
               print '========================'
               for p in oci.getProducts():
                   print p.getchildren()[0].text
        print ''
        return 0, ['']

class OCIInstall:
    def __init__(self,distro,release):
        self.distro = distro
        self.release = release

    def getNames(self):
        return ['oci-install']

    def getUsage(self):
        return "[OCI file]"

    def getSummary(self):
        return "Install packages from OneClickInstall metadata"

    def doCheck(self, base, basecmd, extcmds):
        pass

    def getRepos(self): # so we can act as a "conduit"
        return self.repos

    def show_pkg(self, msg, pkg, md, disp=None):
        print msg, pkg, md

    def show_pkg_exit(self):
        pass

    def doCommand(self, base, basecmd, extcmds):
        packages = []
        repositories = []
        for mp in extcmds:
            et = ElementTree(Element('metapackage'),open(mp))
            oci = OCIMetapackage(et,'%s %s' % (self.distro,self.release))
            for p in oci.getProducts():
                packages.append(get_child_value(p,'name'))
            for r in oci.getRepositories():
                repositories.append((get_child_value(r,'name'),oci.getRepoURL(get_child_value(r,'name'))))
        print packages
        print repositories

        base.verbose_logger.log(logginglevels.INFO_2,
                _("Setting up Install Process"))
        try:
            return base.installPkgs(packages)
        except yum.Errors.YumBaseError, e:
            return 1, [str(e)]

def config_hook(conduit):
    conduit.registerCommand(OCIQuery(distro,release))
    conduit.registerCommand(OCIInstall(distro,release))
